/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.java.decompiler.struct.consts;

import java.io.DataOutputStream;
import java.io.IOException;

/*
 *   NameAndType, FieldRef, MethodRef, InterfaceMethodref
 *   InvokeDynamic, MethodHandle
 */

public class LinkConstant extends PooledConstant {

  // *****************************************************************************
  // public fields
  // *****************************************************************************

  public int index1, index2;

  public String classname;

  public String elementname;

  public String descriptor;

  public int paramCount = 0;

  public boolean isVoid = false;

  public boolean returnCategory2 = false;

  // *****************************************************************************
  // constructors
  // *****************************************************************************

  public LinkConstant(int type, String classname, String elementname, String descriptor) {
    this.type = type;
    this.classname = classname;
    this.elementname = elementname;
    this.descriptor = descriptor;

    initConstant();
  }

  public LinkConstant(int type, int index1, int index2) {
    this.type = type;
    this.index1 = index1;
    this.index2 = index2;
  }

  // *****************************************************************************
  // public methods
  // *****************************************************************************

  public void resolveConstant(ConstantPool pool) {

    if (type == CONSTANT_NameAndType) {
      elementname = pool.getPrimitiveConstant(index1).getString();
      descriptor = pool.getPrimitiveConstant(index2).getString();
    } else if (type == CONSTANT_MethodHandle) {
      LinkConstant ref_info = pool.getLinkConstant(index2);

      classname = ref_info.classname;
      elementname = ref_info.elementname;
      descriptor = ref_info.descriptor;
    } else {
      if (type != CONSTANT_InvokeDynamic) {
        classname = pool.getPrimitiveConstant(index1).getString();
      }

      LinkConstant nametype = pool.getLinkConstant(index2);
      elementname = nametype.elementname;
      descriptor = nametype.descriptor;
    }

    initConstant();
  }

  public void writeToStream(DataOutputStream out) throws IOException {
    out.writeByte(type);
    if (type == CONSTANT_MethodHandle) {
      out.writeByte(index1);
    } else {
      out.writeShort(index1);
    }
    out.writeShort(index2);
  }

  public boolean equals(Object o) {
    if (o == this)
      return true;
    if (o == null || !(o instanceof LinkConstant))
      return false;

    LinkConstant cn = (LinkConstant) o;
    return this.type == cn.type && this.elementname.equals(cn.elementname) && this.descriptor.equals(cn.descriptor)
        && (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname));
  }

  // *****************************************************************************
  // private methods
  // *****************************************************************************

  private void initConstant() {

    if (type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref || type == CONSTANT_InvokeDynamic || type == CONSTANT_MethodHandle) {
      resolveDescriptor(descriptor);
    } else if (type == CONSTANT_Fieldref) {
      returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor));
    }
  }

  private void resolveDescriptor(String descr) {

    String[] arr = descr.split("[()]");
    String par = arr[1];

    int index = 0, counter = 0;
    int len = par.length();

    while (index < len) {

      char c = par.charAt(index);
      if (c == 'L') {
        index = par.indexOf(";", index);
      } else if (c == '[') {
        index++;
        continue;
      }

      counter++;
      index++;
    }

    paramCount = counter;
    isVoid = "V".equals(arr[2]);
    returnCategory2 = ("D".equals(arr[2]) || "J".equals(arr[2]));
  }
}
